home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1996 / MacHack 1996.toast / Hacks / Hacks ’95 / ProcessBar™ / Code / Sources / mdg.c < prev    next >
Encoding:
Text File  |  1995-06-24  |  32.1 KB  |  1,304 lines  |  [TEXT/MMCC]

  1. ////////////////////////////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Portions copyright © 1995 Mark D. Gerl.  All rights reserved worldwide.
  4. //
  5. // Project:        ProcessBar™
  6. //
  7. // File:        mdg.c
  8. //
  9. // Description:    Most of the code written to extend Matt Slot's appe Windows into what it is
  10. //
  11. // Written by:    Mark D. Gerl
  12. //
  13. // Disclaimer:    Written over 40 hours at MacHack '95 -- use at your own risk (actually, I think
  14. //                this code is REALLY stable)!
  15. //
  16. ////////////////////////////////////////////////////////////////////////////////////////////////////
  17.  
  18.  
  19. // Universal headers includes
  20. #include <Icons.h>
  21. #include <LowMem.h>
  22. #include <QDOffscreen.h>
  23.  
  24. // Project-specific includes
  25. #include "Layers.h"
  26. #include "ScriptableFinder.h"
  27. #include "mdg.h"
  28.  
  29.  
  30. // Constants
  31.  
  32. const RGBColor kBgGrayColor = { kTaskbarBgColor, kTaskbarBgColor, kTaskbarBgColor };
  33. const RGBColor kLtGrayColor = { kLtGrayColorValue, kLtGrayColorValue, kLtGrayColorValue };
  34. const RGBColor kDkGrayColor = { kDkGrayColorValue, kDkGrayColorValue, kDkGrayColorValue };
  35.  
  36.  
  37. // Externally visible variables
  38.  
  39. WindowPtr        gTaskbarWindow            = (WindowPtr)0L;
  40. Boolean            gTaskbarVisible            = FALSE;
  41.  
  42.  
  43. // Local statics
  44.  
  45. static Rect        kMacOSIconRect    = {    kTaskbarTopMargin, kTaskbarLeftMargin,
  46.                                     kStandardIconSize+kTaskbarTopMargin,
  47.                                     kStandardIconSize+kTaskbarLeftMargin };
  48.  
  49. static short    gTaskbarNextPosition    = kTaskbarLeftMargin + kStandardIconSize + kTaskbarButtonBorderSize + kTaskbarBetweenMargin;
  50.  
  51. static PSNRec    gPSNInfoArray[kMaxProcesses];
  52. static short    gPSNInfoItemsCount        = 0;
  53. static Rect        gCurrentlyHilitedButtonRect = {0, 0, 0, 0};
  54.  
  55. static GWorldPtr    gBufferWorld = (GWorldPtr)0L;
  56.  
  57.  
  58. //--------------------------------------------------------------------------------------------------
  59. // Functions (not in any particular order)
  60. //--------------------------------------------------------------------------------------------------
  61.  
  62. void SetupOffscreenWorld(void)
  63. {
  64.     OSErr    err;
  65.     Rect    bounds;
  66.     
  67.     // if the offscreen world doesn't exist, make it
  68.     if (gBufferWorld == (GWorldPtr)0L)
  69.     {        
  70.         // quick sanity check
  71.         if (gTaskbarWindow == (WindowPtr)0L) return;
  72.  
  73.         // create a new offscreen world
  74.         bounds = gTaskbarWindow->portRect;
  75.         OffsetRect(&bounds, 0 - bounds.left, 0 - bounds.top);
  76.         err = NewGWorld(&gBufferWorld, 0, &bounds, (CTabHandle)0L, (GDHandle)0L, useTempMem);
  77.         
  78.         // check to see if it was allocated; if not, try again without using temp mem
  79.         if (gBufferWorld == (GWorldPtr)0L)
  80.             err = NewGWorld(&gBufferWorld, 0, &bounds, (CTabHandle)0L, (GDHandle)0L, (GWorldFlags)0);
  81.     }
  82. }
  83.  
  84.  
  85. void UpdateFromOffscreenWorld(Rect *r)
  86. {
  87.     PixMapHandle pm;
  88.     
  89.     // if the offscreen world doesn't exist, try to create it
  90.     if (gBufferWorld == (GWorldPtr)0L)
  91.     {
  92.         SetupOffscreenWorld();
  93.         if (gBufferWorld == (GWorldPtr)0L)
  94.             return;
  95.     }
  96.     
  97.     pm = GetGWorldPixMap(gBufferWorld);
  98.     if (LockPixels(pm))
  99.     {    
  100.         // update from the desired rect (in local coordinates)
  101.         ForeColor(blackColor);
  102.         BackColor(whiteColor);
  103.         CopyBits((BitMap*)(*pm), (BitMap*)&(qd.thePort->portBits), r, r, srcCopy, (RgnHandle)0L);
  104.  
  105.         UnlockPixels(pm);
  106.     }
  107. }
  108.  
  109.  
  110. void Draw3DFrame(Rect * frame, FrameType ft)
  111. {
  112.     Rect r = *frame;
  113.     InsetRect(&r, -(kTaskbarButtonFrameWidth), -(kTaskbarButtonFrameWidth));
  114.  
  115.     PenSize(1, 1);
  116.         
  117.     if (ft == kFrameInset)
  118.     {
  119. //        RGBForeColor(&kDkGrayColor);
  120.         ForeColor(blackColor);
  121.         MoveTo(r.left, r.bottom-1);
  122.         LineTo(r.left, r.top);
  123.         LineTo(r.right-2, r.top);
  124.         MoveTo(r.left+1, r.bottom-2);
  125.         LineTo(r.left+1, r.top+1);
  126.         LineTo(r.right-3, r.top+1);
  127. //        RGBForeColor(&kLtGrayColor);
  128.         ForeColor(whiteColor);
  129.         MoveTo(r.right-1, r.top);
  130.         LineTo(r.right-1, r.bottom-1);
  131.         LineTo(r.left+1, r.bottom-1);
  132.         MoveTo(r.right-2, r.top+1);
  133.         LineTo(r.right-2, r.bottom-2);
  134.         LineTo(r.left+2, r.bottom-2);
  135.     }
  136.     else if (ft == kFrameOutset)
  137.     {
  138. //        RGBForeColor(&kLtGrayColor);
  139.         ForeColor(whiteColor);
  140.         MoveTo(r.left, r.bottom-1);
  141.         LineTo(r.left, r.top);
  142.         LineTo(r.right-2, r.top);
  143.         MoveTo(r.left+1, r.bottom-2);
  144.         LineTo(r.left+1, r.top+1);
  145.         LineTo(r.right-3, r.top+1);
  146. //        RGBForeColor(&kDkGrayColor);
  147.         ForeColor(blackColor);
  148.         MoveTo(r.right-1, r.top);
  149.         LineTo(r.right-1, r.bottom-1);
  150.         LineTo(r.left+1, r.bottom-1);
  151.         MoveTo(r.right-2, r.top+1);
  152.         LineTo(r.right-2, r.bottom-2);
  153.         LineTo(r.left+2, r.bottom-2);
  154.     }
  155.     else
  156.     {
  157.         DebugStr("\pDraw3DFrame- illegal frame type passed");
  158.     }
  159.         
  160.     BackColor(whiteColor);
  161.     ForeColor(blackColor);
  162. }
  163.  
  164.  
  165. void ShowTaskbar(WindowPtr win)
  166. {
  167.     if ((win != (WindowPtr)0L) && !gTaskbarVisible)
  168.     {
  169.         short i;
  170.         for (i = 1 + kTaskbarScreenBottomMargin; i <= kTaskbarHeight + 1; i += 4)
  171.         {
  172.             MoveWindow(win, qd.screenBits.bounds.left, qd.screenBits.bounds.bottom - i, FALSE);
  173.             DrawTaskbar(win);
  174.             SystemTask();
  175.         }
  176.         gTaskbarVisible = TRUE;
  177.     }
  178. }
  179.  
  180.  
  181. void HideTaskbar(WindowPtr win)
  182. {
  183.     if ((win != (WindowPtr)0L) && gTaskbarVisible)
  184.     {
  185.         short i;
  186.         for (i = 1; i <= kTaskbarHeight - kTaskbarScreenBottomMargin + 1; i += 4)
  187.         {
  188.             MoveWindow(win, qd.screenBits.bounds.left, qd.screenBits.bounds.bottom-kTaskbarHeight + i, FALSE);
  189.             DrawTaskbar(win);
  190.             SystemTask();
  191.         }
  192.         gTaskbarVisible = FALSE;
  193.     }
  194. }
  195.  
  196.  
  197. void ToggleTaskbarVisibility(WindowPtr win)
  198. {
  199.     if (gTaskbarVisible == TRUE)
  200.         HideTaskbar(win);
  201.     else
  202.         ShowTaskbar(win);
  203. }
  204.  
  205.  
  206. Boolean FindFirstVisibleProcess(ProcessSerialNumber *psn)
  207. {
  208.     ProcessSerialNumber newPSN, curPSN;
  209.     ProcessInfoRec    psInfo;
  210.     LayerPtr lp;
  211.     OSErr    err;
  212.     Boolean    result = FALSE, same;
  213.  
  214.     err = GetCurrentProcess(&curPSN);
  215.  
  216.     // loop over the processes looking for a visible process (that's not current or psn)
  217.     newPSN.highLongOfPSN = newPSN.lowLongOfPSN = kNoProcess;
  218.     do
  219.     {
  220.         err = GetNextProcess(&newPSN);
  221.         if (err != noErr) break;
  222.  
  223.         // get the process information
  224.         psInfo.processInfoLength = sizeof(ProcessInfoRec);
  225.         psInfo.processName         = 0L;
  226.         psInfo.processAppSpec     = 0L;
  227.         err = GetProcessInformation(&newPSN, &psInfo);
  228.         if (err != noErr)
  229.         {
  230. //            DebugStr("\pFindFirstVisibleProcess- GetProcessInformation() failed;g");
  231.             break;
  232.         }
  233.         else if (psInfo.processMode & modeOnlyBackground)
  234.             continue;
  235.  
  236.         // ignore this process if it's our process
  237.         err = SameProcess(&curPSN, &newPSN, &same);
  238.         if (same == TRUE)
  239.             continue;
  240.         
  241.         // ignore this process if it's the same as the one being hidden
  242.         err = SameProcess(psn, &newPSN, &same);
  243.         if (same == TRUE)
  244.             continue;
  245.         
  246.         lp = ProcessLayer(&newPSN);
  247.         if (HiddenLayer(lp))
  248.         {
  249.             *psn = newPSN;
  250.             result = TRUE;
  251.             break;
  252.         }
  253.  
  254.     } while (err == noErr);
  255.  
  256.     return result;
  257. }
  258.  
  259.  
  260. void TogglePSNVisibility(ProcessSerialNumber *psn)
  261. {
  262.     ProcessSerialNumber frontPSN;
  263.     OSErr err;
  264.     Boolean result;
  265.     short index;
  266.  
  267.     LayerPtr lp = ProcessLayer(psn);
  268.     
  269.     // if visible, then we're trying to hide it...
  270.     if (HiddenLayer(lp))
  271.     {
  272.         // check to see if we're trying to hide the current front process
  273.         err = GetFrontProcess(&frontPSN);
  274.         err = SameProcess(psn, &frontPSN, &result);
  275.         // if yes, make sure there is another non-hidden process to go to...
  276.         if (result == TRUE)
  277.         {
  278.             if (FindFirstVisibleProcess(&frontPSN))
  279.             {
  280.                 err = SetFrontProcess(&frontPSN);
  281.                 ShowHideLayer(lp, FALSE);
  282.             }
  283.             // else, sysbeep, because we cannot hide the last visible process
  284.             else
  285.                 SysBeep(1);
  286.         }
  287.         else
  288.             ShowHideLayer(lp, !HiddenLayer(lp));
  289.     }
  290.     else
  291.         ShowHideLayer(lp, !HiddenLayer(lp));
  292.  
  293.     // update the icon for this process
  294.     if (FindPSNInProcessArray(psn, &index))
  295.     {
  296.         LayerPtr lp = ProcessLayer(psn);
  297.         Rect    temp = gPSNInfoArray[index].buttonRect;
  298.         
  299.         temp.right = temp.left + kStandardIconSize;
  300.         err = PlotIconSuite(&temp, atLeft, HiddenLayer(lp) ? ttNone : ttDisabled, gPSNInfoArray[index].iconSuite);
  301.     }
  302. }
  303.  
  304.  
  305. void BringPSNToFront(ProcessSerialNumber *psn)
  306. {
  307.     short index;
  308.     
  309.     UpdateProcessArray();
  310.     
  311.     if (FindPSNInProcessArray(psn, &index))
  312.         SetFrontProcess(psn);
  313. }
  314.  
  315.  
  316. void Remove1TaskbarProcess(short processArrayIndex)
  317. {
  318.     RgnHandle            savedClip;
  319.     GrafPtr                savedPort;
  320.     Rect                inval = {0, 0, 0, 0};
  321.     short                j, shiftDelta;
  322.     OSErr                err;
  323.     
  324.     // quick sanity checks
  325.     if (gTaskbarWindow == (WindowPtr)0L) return;
  326.     if ((processArrayIndex < 0) || (processArrayIndex >= gPSNInfoItemsCount)) return;
  327.  
  328.     GetPort(&savedPort);
  329.     SetPort(gTaskbarWindow);
  330.     
  331.     // determine the (graphical) shift in pixels
  332.     shiftDelta = (gPSNInfoArray[processArrayIndex].buttonRect.right - gPSNInfoArray[processArrayIndex].buttonRect.left) + kTaskbarBetweenMargin;
  333.     
  334.     // deallocate any necessary memory
  335.     err = DisposeIconSuite(gPSNInfoArray[processArrayIndex].iconSuite, TRUE);
  336.     
  337.     // invalidate the area to the right of the first shifted button
  338.     inval = gPSNInfoArray[processArrayIndex].buttonRect;
  339.     inval.right = gTaskbarWindow->portRect.right;
  340.     InsetRect(&inval, -(kTaskbarButtonBorderSize), -(kTaskbarButtonBorderSize));
  341.     InvalRect(&inval);
  342.  
  343.     // adjust the index for next
  344.     gPSNInfoItemsCount--;
  345.     
  346.     // if we need to compact the array
  347.     if (processArrayIndex < gPSNInfoItemsCount)
  348.     {
  349.         // we need to shift the remaining array elements up in the array (if necessary)
  350.         BlockMoveData((Ptr)&gPSNInfoArray[processArrayIndex+1], (Ptr)&gPSNInfoArray[processArrayIndex], (gPSNInfoItemsCount - processArrayIndex) * sizeof(PSNRec));
  351.         
  352.         // adjust all the button rects to reflect the shift
  353.         for (j = processArrayIndex; j < gPSNInfoItemsCount; j++)
  354.         {
  355.             gPSNInfoArray[j].buttonRect.left  -= shiftDelta;
  356.             gPSNInfoArray[j].buttonRect.right -= shiftDelta;
  357.         }
  358.     }
  359.  
  360.     // adjust the rect location for the next new process button
  361.     gTaskbarNextPosition -= shiftDelta;
  362.     
  363.     // refresh display (if necessary)
  364.     if (!EmptyRect(&inval))
  365.     {
  366.         savedClip = NewRgn();
  367.         if (savedClip != (RgnHandle)0L)
  368.         {
  369.             GetClip(savedClip);
  370.             ClipRect(&inval);
  371.             
  372.             DrawTaskbar(gTaskbarWindow);
  373.             
  374.             SetClip(savedClip);
  375.             DisposeRgn(savedClip);
  376.         }
  377.         else
  378.         {
  379.             DrawTaskbar(gTaskbarWindow);
  380.         }
  381.     }
  382.  
  383.     SetPort(savedPort);
  384. }
  385.  
  386.  
  387. void RemovePSNFromProcessArray(ProcessSerialNumber *psn)
  388. {
  389.     short    i;
  390.     OSErr    err;
  391.     Boolean    result;
  392.     
  393.     for (i = 0; i < gPSNInfoItemsCount; i++)
  394.     {
  395.         err = SameProcess(psn, &gPSNInfoArray[i].psn, &result);
  396.         if (result == TRUE)
  397.         {
  398.             Remove1TaskbarProcess(i);
  399.             break;
  400.         }
  401.     }
  402. }
  403.  
  404.  
  405. Boolean FindPSNInProcessArray(ProcessSerialNumber *psn, short *processArrayIndex)
  406. {
  407.     Boolean    result = FALSE;
  408.     short    i;
  409.     OSErr    err;
  410.     
  411.     for (i = 0; i < gPSNInfoItemsCount; i++)
  412.     {
  413.         err = SameProcess(psn, &gPSNInfoArray[i].psn, &result);
  414.         if (result == TRUE)
  415.         {
  416.             *processArrayIndex = i;
  417.             break;
  418.         }
  419.     }
  420.  
  421.     return result;
  422. }
  423.  
  424.  
  425. Boolean FindPSNForClick(Point where, ProcessSerialNumber *psn, short *processArrayIndex)
  426. {
  427.     Boolean    result = FALSE;
  428.     short    i;
  429.     
  430.     for (i = 0; i < gPSNInfoItemsCount; i++)
  431.     {
  432.         if (PtInRect(where, &gPSNInfoArray[i].buttonRect))
  433.         {
  434.             *psn = gPSNInfoArray[i].psn;
  435.             *processArrayIndex = i;
  436.             result = TRUE;
  437.             break;
  438.         }
  439.     }
  440.  
  441.     return result;
  442. }
  443.  
  444.  
  445. Boolean IsProcessAlive(ProcessSerialNumber *psn)
  446. {
  447.     ProcessInfoRec        psInfo;
  448.     
  449.     // get the process information
  450.     psInfo.processInfoLength = sizeof(ProcessInfoRec);
  451.     psInfo.processName         = 0L;                            // I don't care about this
  452.     psInfo.processAppSpec     = 0L;                            // I don't care about this
  453.  
  454.     return (GetProcessInformation(psn, &psInfo) == noErr);
  455. }
  456.  
  457.  
  458. void UpdateProcessArray(void)
  459. {
  460.     ProcessSerialNumber    psn, frontPSN;
  461.     RgnHandle            savedClip;
  462.     GrafPtr                savedPort;
  463.     Rect                inval = {0, 0, 0, 0};
  464.     short                i, j, shiftDelta, index;
  465.     OSErr                err;
  466.  
  467.     // quick sanity check
  468.     if (gTaskbarWindow == (WindowPtr)0L) return;
  469.  
  470.     GetPort(&savedPort);
  471.     SetPort(gTaskbarWindow);
  472.     
  473.     // first, remove any processes that are no longer valid
  474.     for (i = 0; i < gPSNInfoItemsCount; i++)
  475.     {
  476.         if (!IsProcessAlive(&(gPSNInfoArray[i].psn)))
  477.         {
  478.             // check to see if this is the currently hilited button rect
  479.             if (EqualRect(&gCurrentlyHilitedButtonRect, &gPSNInfoArray[i].buttonRect))
  480.             {
  481.                 gCurrentlyHilitedButtonRect.left = gCurrentlyHilitedButtonRect.right =
  482.                 gCurrentlyHilitedButtonRect.top = gCurrentlyHilitedButtonRect.bottom = 0;
  483.             }
  484.                 
  485.             // determine the (graphical) shift in pixels
  486.             shiftDelta = (gPSNInfoArray[i].buttonRect.right - gPSNInfoArray[i].buttonRect.left) + kTaskbarBetweenMargin;
  487.             
  488.             // deallocate any necessary memory
  489.             err = DisposeIconSuite(gPSNInfoArray[i].iconSuite, TRUE);
  490.             
  491.             // invalidate the area to the right of the first shifted button
  492.             inval = gPSNInfoArray[i].buttonRect;
  493.             inval.right = gTaskbarWindow->portRect.right;
  494.             InsetRect(&inval, -(kTaskbarButtonBorderSize), -(kTaskbarButtonBorderSize));
  495.             InvalRect(&inval);
  496.  
  497.             // adjust the index for next
  498.             gPSNInfoItemsCount--;
  499.             
  500.             // if we need to compact the array
  501.             if (i < gPSNInfoItemsCount)
  502.             {
  503.                 // we need to shift the remaining array elements up in the array (if necessary)
  504.                 BlockMoveData((Ptr)&gPSNInfoArray[i+1], (Ptr)&gPSNInfoArray[i], (gPSNInfoItemsCount - i) * sizeof(PSNRec));
  505.                 
  506.                 // adjust all the button rects to reflect the shift
  507.                 for (j = i; j < gPSNInfoItemsCount; j++)
  508.                 {
  509.                     gPSNInfoArray[j].buttonRect.left  -= shiftDelta;
  510.                     gPSNInfoArray[j].buttonRect.right -= shiftDelta;
  511.                 }
  512.             }
  513.  
  514.             // adjust the rect location for the next new process button
  515.             gTaskbarNextPosition -= shiftDelta;
  516.         }
  517.     }
  518.     
  519.     // refresh display (if necessary)
  520.     if (!EmptyRect(&inval))
  521.     {
  522.         savedClip = NewRgn();
  523.         if (savedClip != (RgnHandle)0L)
  524.         {
  525.             GetClip(savedClip);
  526.             ClipRect(&inval);
  527.             
  528.             DrawTaskbar(gTaskbarWindow);
  529.             
  530.             SetClip(savedClip);
  531.             DisposeRgn(savedClip);
  532.         }
  533.         else
  534.         {
  535.             DrawTaskbar(gTaskbarWindow);
  536.         }
  537.     }
  538.     
  539.     // loop over the processes looking for new processes to add
  540.     psn.highLongOfPSN = psn.lowLongOfPSN = kNoProcess;
  541.     do
  542.     {
  543.         err = GetNextProcess(&psn);
  544.         if (err != noErr) break;
  545.             
  546.         if (!FindPSNInProcessArray(&psn, &index))
  547.             AddProcessToTaskbar(&psn);
  548.             
  549.     } while (err == noErr);
  550.     
  551.     HiliteCurrentProcess();
  552.     AdjustIcons();
  553.  
  554.     SetPort(savedPort);
  555. }
  556.  
  557.  
  558. void AdjustIcons(void)
  559. {
  560.     short i;
  561.         
  562.     for (i = 0; i < gPSNInfoItemsCount; i++)
  563.     {
  564.         LayerPtr    lp = ProcessLayer(&gPSNInfoArray[i].psn);
  565.         Rect        temp = gPSNInfoArray[i].buttonRect;
  566.         OSErr        err;
  567.         Boolean        processIsHidden;
  568.         
  569.         temp.right = temp.left + kStandardIconSize;
  570.         processIsHidden = !HiddenLayer(lp);
  571.         if (gPSNInfoArray[i].iconIsHilited == processIsHidden)
  572.             err = PlotIconSuite(&temp, atLeft, processIsHidden ? ttDisabled : ttNone, gPSNInfoArray[i].iconSuite);
  573.     }
  574. }
  575.  
  576.  
  577. void HiliteCurrentProcess(void)
  578. {
  579.     ProcessSerialNumber    frontPSN;
  580.     short                index;
  581.     OSErr                err;
  582.  
  583.     // make sure we have the front process framed correctly
  584.     err = GetFrontProcess(&frontPSN);
  585.     if (FindPSNInProcessArray(&frontPSN, &index))
  586.     {
  587.         if (!EqualRect(&gCurrentlyHilitedButtonRect, &gPSNInfoArray[index].buttonRect))
  588.         {
  589.             short i;
  590.             
  591.             gCurrentlyHilitedButtonRect = gPSNInfoArray[index].buttonRect;
  592.             
  593.             for (i = 0; i < gPSNInfoItemsCount; i++)
  594.                 Draw3DFrame(&gPSNInfoArray[i].buttonRect, (i == index) ? kFrameInset : kFrameOutset);
  595.         }
  596.     }
  597.     else // the front process is ourselves, and we don't have a button
  598.     {
  599.         gCurrentlyHilitedButtonRect.left = gCurrentlyHilitedButtonRect.right  =
  600.         gCurrentlyHilitedButtonRect.top  = gCurrentlyHilitedButtonRect.bottom = 0;
  601.     }
  602. }
  603.  
  604.  
  605. void IdleTaskbar(void)
  606. {
  607.     static const long    kIdleThreshold    = 20L;
  608.  
  609.     static GrafPtr        savedPort        = (GrafPtr)0L;
  610.     static Point        localPt            = {0, 0};
  611.     static Boolean        overMyWindow    = FALSE;
  612.     static long            ticks            = 0L;
  613.     
  614.     if (LMGetTicks() >= (long)(ticks + kIdleThreshold))
  615.     {
  616.         // quick sanity check
  617.         if (gTaskbarWindow == (WindowPtr)0L) return;
  618.         
  619.         // look where the mouse is, and roll up/down the taskbar if necessary
  620.         GetPort(&savedPort);
  621.         SetPort(gTaskbarWindow);
  622.         
  623.         GetMouse(&localPt);
  624.         overMyWindow = PtInRect(localPt, &(gTaskbarWindow->portRect));
  625.         if (overMyWindow && (gTaskbarVisible == FALSE))
  626.             ToggleTaskbarVisibility(gTaskbarWindow);
  627.         else if (!overMyWindow && (gTaskbarVisible == TRUE))
  628.             ToggleTaskbarVisibility(gTaskbarWindow);
  629.         
  630.         SetPort(savedPort);
  631.         
  632.         UpdateProcessArray();
  633.         
  634.         ticks = LMGetTicks();
  635.     }
  636. }
  637.  
  638.  
  639. void DoMacOSIconClick(EventRecord *theEvent)
  640. {
  641.     MenuHandle    myPopupMenuHandle = NULL;
  642.     long    mresult;
  643.     short    x = qd.screenBits.bounds.bottom - kTaskbarHeight;
  644.     short    y = qd.screenBits.bounds.left + 2;
  645.     short    lastItemSelected = 6;
  646.     
  647.     myPopupMenuHandle = GetMenu(150);
  648.     if (myPopupMenuHandle)
  649.         {
  650.         InsertMenu( myPopupMenuHandle, hierMenu);
  651.         mresult = PopUpMenuSelect(myPopupMenuHandle, x, y, lastItemSelected);
  652.         DeleteMenu(150);
  653.         
  654.         if (HiWord(mresult) != 0) //something was selected
  655.             {
  656.             switch (LoWord(mresult))
  657.                 {
  658.                 case 1:    // about
  659. //                    if (theEvent->modifiers & optionKey)
  660. //                        SendQuitToSelf();
  661. //                    else
  662.                     {
  663.                         ProcessSerialNumber curPSN, savedPSN;
  664.                         OSErr err;
  665.                         
  666.                         err = GetFrontProcess(&savedPSN);
  667.                         if (err != noErr) return;
  668.                         
  669.                         err = GetCurrentProcess(&curPSN);
  670.                         if (err != noErr) return;
  671.                         
  672.                         err = SetFrontProcess(&curPSN);
  673.                         
  674.                         // attempt to bring ourself front before proceeding
  675.                         PullApplicationToFront();
  676.                         
  677.                         Alert(128, 0);
  678.                         
  679.                         err = SetFrontProcess(&savedPSN);
  680.                     }
  681.                     break;
  682.                 case 3:    // quit
  683.                     SendQuitToSelf();
  684.                     break;
  685.                 case 5:    // restart
  686.                     SendRestartToFinder();
  687.                     break;
  688.                 case 6:    // shutdown
  689.                     SendShutDownToFinder();
  690.                     break;
  691.                 }
  692.             }
  693.             ;
  694.         }
  695.  
  696.  
  697. }
  698.  
  699.  
  700. Boolean TrackTaskbarButtonClick(EventRecord *theEvent, Rect *box)
  701. {
  702.     // note, theEvent->where is already in local coordinates and the port is set up
  703.     
  704.     Point    localPt;
  705.     Boolean result = FALSE;
  706.     
  707.     if (StillDown())
  708.     {
  709.         Boolean inRect = TRUE;
  710.         
  711.         while (WaitMouseUp())
  712.         {
  713.             SystemTask();
  714.             GetMouse(&localPt);
  715.             if (PtInRect(localPt, box))
  716.             {
  717.                 if (inRect == TRUE) continue;
  718.                 
  719.                 Draw3DFrame(box, kFrameInset);
  720.                 
  721.                 inRect = TRUE;
  722.             }
  723.             else
  724.             {
  725.                 if (inRect == FALSE) continue;
  726.  
  727.                 Draw3DFrame(box, kFrameOutset);
  728.                 
  729.                 inRect = FALSE;
  730.             }
  731.         }
  732.         
  733.         // now that the mouse has been released, set the real result
  734.         GetMouse(&localPt);
  735.         result = PtInRect(localPt, box);
  736.         
  737.         // fix up any left over drawing
  738.         if ((result == FALSE) && (inRect == TRUE))
  739.         {
  740.             // we don't want to draw an outset frame if this button represents the current process
  741.             ProcessSerialNumber frontPSN, psn;
  742.             OSErr err;
  743.             short index;
  744.             if (FindPSNForClick(localPt, &psn, &index))
  745.             {
  746.                 Boolean result;
  747.                 err = GetFrontProcess(&frontPSN);
  748.                 err = SameProcess(&frontPSN, &psn, &result);
  749.                 if (result != TRUE)
  750.                     Draw3DFrame(box, kFrameOutset);
  751.             }
  752.             else
  753.                 Draw3DFrame(box, kFrameOutset);
  754.         }
  755.         else if ((result == TRUE) && (inRect == FALSE))
  756.             Draw3DFrame(box, kFrameInset);
  757.     }
  758.     
  759.     return result;
  760. }
  761.  
  762.  
  763. void DoClickInTaskbar(EventRecord *theEvent, WindowPtr win)
  764. {
  765.     ProcessSerialNumber psn;
  766.     GrafPtr                savePort;
  767.     short                index;
  768.     
  769.     GetPort(&savePort);
  770.     SetPort(win);
  771.     
  772.     GlobalToLocal(&(theEvent->where));
  773.     if (PtInRect(theEvent->where, &kMacOSIconRect))
  774.     {
  775.         DoMacOSIconClick(theEvent);
  776.     }
  777.     else if (FindPSNForClick(theEvent->where, &psn, &index))
  778.     {
  779.         if (TrackTaskbarButtonClick(theEvent, &(gPSNInfoArray[index].buttonRect)))
  780.         {
  781.             UpdateProcessArray();
  782.             
  783.             if (theEvent->modifiers & optionKey)
  784.                 TogglePSNVisibility(&psn);
  785.             else if (theEvent->modifiers & cmdKey)
  786.             {
  787.                 SendQuitToPSN(&psn);
  788.                 Remove1TaskbarProcess(index);
  789.             }
  790.             else
  791.                 BringPSNToFront(&psn);
  792.         }
  793.         
  794.         HiliteCurrentProcess();
  795.         AdjustIcons();
  796.     }
  797.     
  798.     SetPort(savePort);
  799. }
  800.  
  801.  
  802. void AddProcessToTaskbar(ProcessSerialNumber *psn)
  803. {
  804.     ProcessSerialNumber    curPSN, frontPSN;
  805.     ProcessInfoRec        psInfo;
  806.     FSSpec                appSpec;
  807.     Str31                appName;
  808.     Rect                r;
  809.     OSErr                err;
  810.     short                appNameWidth;
  811.     FontInfo            fInfo;
  812.     Boolean                result;
  813.     
  814.     // get the process information
  815.     psInfo.processInfoLength = sizeof(ProcessInfoRec);
  816.     psInfo.processName         = appName;
  817.     psInfo.processAppSpec     = &appSpec;
  818.     err = GetProcessInformation(psn, &psInfo);
  819.     if (err != noErr)
  820.     {
  821. //        DebugStr("\pAddProcessToTaskbar- GetProcessInformation() failed;g");
  822.         return;
  823.     }
  824.     else if (((psInfo.processType != 'APPL') && (psInfo.processType != 'FNDR')) ||
  825.             (psInfo.processMode & modeOnlyBackground))
  826.         return;
  827.     else
  828.     {
  829.         err = GetCurrentProcess(&curPSN);
  830.         err = SameProcess(&curPSN, psn, &result);
  831.         if (result == TRUE)
  832.             return;
  833.     }
  834.         
  835.     // determine the width of the name
  836.     TextSize(kTaskbarFontSize);
  837.     TextFont(kTaskbarFont);
  838.     TextFace(kTaskbarFontStyle);
  839.     appNameWidth = StringWidth(appName);
  840.     GetFontInfo(&fInfo);
  841.     
  842.     // determine this process' button's bounding box
  843.     r.left        = gTaskbarNextPosition;
  844.     r.right        = r.left + appNameWidth + (kTaskbarButtonBorderSize << 1) + 4 + kStandardIconSize;
  845.     r.top        = kTaskbarTopMargin;
  846.     r.bottom    = kTaskbarHeight - kTaskbarTopMargin;
  847.     
  848.     // bump up the next processes position
  849.     gTaskbarNextPosition = r.right + kTaskbarBetweenMargin;
  850.     
  851.     // draw the frame (depends on being the front process or not)
  852.     err = GetFrontProcess(&frontPSN);
  853.     err = SameProcess(&frontPSN, psn, &result);
  854.     Draw3DFrame(&r, (result == TRUE) ? kFrameInset : kFrameOutset);
  855.  
  856.     // remember this info in our process array
  857.     gPSNInfoArray[gPSNInfoItemsCount].psn         = *psn;
  858.     gPSNInfoArray[gPSNInfoItemsCount].buttonRect = r;
  859.     err = GetScriptableFinderFileIcon (&appSpec, FALSE, kAEWaitReply, &(gPSNInfoArray[gPSNInfoItemsCount].iconSuite));
  860.  
  861.     // draw the icon (thanks Leonard!)
  862.     if (err == noErr)
  863.     {
  864.         LayerPtr lp = ProcessLayer(psn);
  865.         Rect    temp = r;
  866.         
  867.         temp.right = temp.left + kStandardIconSize;
  868.         
  869.         err = PlotIconSuite(&temp, atLeft, HiddenLayer(lp) ? ttNone : ttDisabled, gPSNInfoArray[gPSNInfoItemsCount].iconSuite);
  870.     }
  871.     
  872.     // draw the text
  873.     r.left  += 4 + kStandardIconSize + 2;
  874.     r.right -= 4;
  875.     r.top    = kTaskbarTopMargin + (((r.bottom - r.top) >> 1) - ((fInfo.ascent + fInfo.descent + fInfo.leading) >> 1));
  876.     r.bottom = r.top + fInfo.ascent + fInfo.descent + fInfo.leading + 1;
  877.     RGBBackColor(&kBgGrayColor);
  878.     TETextBox((Ptr)(appName+1),(long)(*appName),&r,teJustCenter);
  879.  
  880.     // increment the array index (but avoid an overflow)
  881.     gPSNInfoItemsCount++;
  882.     if (gPSNInfoItemsCount > kMaxProcesses)
  883.         gPSNInfoItemsCount = kMaxProcesses;
  884.         
  885. }
  886.  
  887.  
  888. void DrawMacOSIcon()
  889. {
  890.     GrafPtr                savePort;
  891.     Rect                tempRect = kMacOSIconRect;
  892.     OSErr                err;
  893.  
  894.     // quick sanity check
  895.     if (gTaskbarWindow == (WindowPtr)0L) return;
  896.  
  897. //    GetPort(&savePort);
  898. //    SetPort(gTaskbarWindow);
  899.  
  900.     RGBBackColor(&kBgGrayColor);
  901.     EraseRect(&tempRect);
  902.     
  903.     // draw the icon
  904.     err = PlotIconID(&kMacOSIconRect, atNone, ttNone, kMacOSIconRsrcID);
  905.     
  906.     // draw the frame (3D coming out at you)
  907.     Draw3DFrame(&kMacOSIconRect, kFrameOutset);
  908.  
  909.     BackColor(whiteColor);
  910.  
  911.     InsetRect(&tempRect, -(kTaskbarButtonFrameWidth), -(kTaskbarButtonFrameWidth));    // adjust for frame
  912. //    ValidRect(&tempRect);
  913.     
  914. //    SetPort(savePort);
  915. }
  916.  
  917.  
  918. void Draw1TaskbarProcess(short processArrayIndex)
  919. {
  920.     ProcessSerialNumber    frontPSN;
  921.     ProcessInfoRec        psInfo;
  922.     FSSpec                appSpec;
  923.     Str31                appName;
  924.     Rect                r, valid;
  925.     OSErr                err;
  926.     short                appNameWidth;
  927.     FontInfo            fInfo;
  928.     Boolean                result;
  929.     
  930.     if (processArrayIndex >= gPSNInfoItemsCount)
  931.     {
  932.         DebugStr("\pDraw1TaskbarProcess- illegal input param");
  933.         return;
  934.     }
  935.     
  936.     // get the process information
  937.     psInfo.processInfoLength = sizeof(ProcessInfoRec);
  938.     psInfo.processName         = appName;
  939.     psInfo.processAppSpec     = &appSpec;
  940.     err = GetProcessInformation(&(gPSNInfoArray[processArrayIndex].psn), &psInfo);
  941.     if (err != noErr)
  942.     {
  943. //        DebugStr("\pDraw1TaskbarProcess- GetProcessInformation() failed;g");
  944.         return;
  945.     }
  946.         
  947.     // determine the width of the name
  948.     TextSize(kTaskbarFontSize);
  949.     TextFont(kTaskbarFont);
  950.     TextFace(kTaskbarFontStyle);
  951.     appNameWidth = StringWidth(appName);
  952.     GetFontInfo(&fInfo);
  953.     
  954.     // determine this process' button's bounding box
  955.     r.left        = gTaskbarNextPosition;
  956.     r.right        = r.left + appNameWidth + (kTaskbarButtonBorderSize << 1) + 4 + kStandardIconSize;
  957.     r.top        = kTaskbarTopMargin;
  958.     r.bottom    = kTaskbarHeight - kTaskbarTopMargin;
  959.     
  960.     // bump up the next processes position
  961.     gTaskbarNextPosition = r.right + kTaskbarBetweenMargin;
  962.     
  963.     // save a copy of the rect for later validation
  964.     valid = r;
  965.     
  966.     // draw the frame (depends on being the front process or not)
  967.     err = GetFrontProcess(&frontPSN);
  968.     err = SameProcess(&frontPSN, &gPSNInfoArray[processArrayIndex].psn, &result);
  969.     Draw3DFrame(&r, (result == TRUE) ? kFrameInset : kFrameOutset);
  970.  
  971.     // draw the icon (thanks Leonard!)
  972.     if (err == noErr)
  973.     {
  974.         LayerPtr lp = ProcessLayer(&gPSNInfoArray[processArrayIndex].psn);
  975.         Rect    r2 = r;
  976.         
  977.         r2.right = r2.left + kStandardIconSize;
  978.         err = PlotIconSuite(&r2, atLeft, HiddenLayer(lp) ? ttNone : ttDisabled, gPSNInfoArray[processArrayIndex].iconSuite);
  979.     }
  980.     
  981.     // draw the text
  982.     r.left  += 4 + kStandardIconSize + 2;
  983.     r.right -= 4;
  984.     r.top    = kTaskbarTopMargin + (((r.bottom - r.top) >> 1) - ((fInfo.ascent + fInfo.descent + fInfo.leading) >> 1));
  985.     r.bottom = r.top + fInfo.ascent + fInfo.descent + fInfo.leading + 1;
  986.     RGBBackColor(&kBgGrayColor);
  987.     TETextBox((Ptr)(appName+1),(long)(*appName),&r,teJustCenter);
  988.     
  989.     // validate the drawing
  990.     InsetRect(&valid, -(kTaskbarButtonFrameWidth), -(kTaskbarButtonFrameWidth));    // adjust for frame
  991. //    ValidRect(&valid);
  992. }
  993.  
  994.  
  995. void DrawTaskbar(WindowPtr win)
  996. {
  997.     GrafPtr                savePort, workingPort;
  998.     PixMapHandle        pm;
  999.     OSErr                err;
  1000.     ProcessSerialNumber    psn, myPSN;
  1001.     short                i;
  1002.     Boolean                buffering;
  1003.  
  1004.     GetPort(&savePort);
  1005.  
  1006.     // attempt to use offscreen buffering
  1007.     SetupOffscreenWorld();
  1008.     buffering = (gBufferWorld != (GWorldPtr)0L);
  1009.     
  1010.     if (buffering)
  1011.     {
  1012.         pm = GetGWorldPixMap(gBufferWorld);
  1013.         LockPixels(pm);
  1014.         SetPort((GrafPtr)gBufferWorld);
  1015.         workingPort = (GrafPtr)gBufferWorld;
  1016.     }
  1017.     else
  1018.     {
  1019.         SetPort(win);
  1020.         workingPort = (GrafPtr)win;
  1021.     }
  1022.         
  1023.  
  1024.     RGBBackColor(&kBgGrayColor);
  1025.     EraseRect(&workingPort->portRect);
  1026.     
  1027.     // draw the icon
  1028.     DrawMacOSIcon();
  1029.     
  1030.     // set the inital first position for locating the process buttons
  1031.     gTaskbarNextPosition = kTaskbarLeftMargin + kStandardIconSize + kTaskbarButtonBorderSize + kTaskbarBetweenMargin;
  1032.  
  1033.     for (i = 0; i < gPSNInfoItemsCount; i++)
  1034.         Draw1TaskbarProcess(i);
  1035.  
  1036.     BackColor(whiteColor);
  1037.     ForeColor(blackColor);
  1038.     
  1039.     // if we're using offscreen buffering, update the window here
  1040.     if (buffering)
  1041.     {
  1042.         UnlockPixels(pm);
  1043.         SetPort(win);
  1044.         UpdateFromOffscreenWorld(&workingPort->portRect);
  1045.     }
  1046.  
  1047. //    ValidRect(&win->portRect);
  1048.     SetPort(savePort);
  1049. }
  1050.  
  1051.  
  1052. // The following code was nabbed from MacApp
  1053.  
  1054. void PullApplicationToFront()
  1055. {
  1056.     EventRecord theEvent;
  1057.     short        i;
  1058.  
  1059.     // The "Programmer's guide to MultiFinder says make an event call several times. I
  1060.     // guess 3 calls counts as several. Also, it says call GetNextEvent but we don't want
  1061.     // to lose events on the floor so we use EventAvail since it seems to work OK
  1062.     for (i = 1; i <= 3; ++i)
  1063.         EventAvail(everyEvent, &theEvent);
  1064. } // PullApplicationToFront 
  1065.  
  1066. static Boolean IsThisKeyDown(const short theKey)
  1067. {
  1068.     KeyMap km;
  1069.     Byte asBytes[16];
  1070.  
  1071.     GetKeys(km);
  1072.     BlockMove((Ptr)km, (Ptr)asBytes, 16);
  1073.     return (asBytes[theKey >> 3] & (1 << (theKey & 0x07))) ? TRUE : FALSE;
  1074. } // IsThisKeyDown 
  1075.  
  1076. Boolean IsCommandKeyDown()
  1077. {
  1078.     const short kCommandKey = 55;
  1079.     return IsThisKeyDown(kCommandKey);
  1080. } // IsCommandKeyDown 
  1081.  
  1082. Boolean IsControlKeyDown()
  1083. {
  1084.     const short kCtlKey = 0x3B;
  1085.     return IsThisKeyDown(kCtlKey);
  1086. } // IsControlKeyDown 
  1087.  
  1088. Boolean IsOptionKeyDown()
  1089. {
  1090.     const short kOptionKey = 58;
  1091.     return IsThisKeyDown(kOptionKey);
  1092. } // IsOptionKeyDown 
  1093.  
  1094. Boolean IsShiftKeyDown()
  1095. {
  1096.     const short kShiftKey = 56;
  1097.     return IsThisKeyDown(kShiftKey);
  1098. } // IsShiftKeyDown 
  1099.  
  1100.  
  1101. // The following code was borrowed from DropShell 2.0 sources (thanks Leonard!)...
  1102.  
  1103. /*** These routines use the Process Manager to give you information about yourself ***/
  1104.  
  1105. void GetMyAppName(Str255 appName)    {
  1106.     OSErr                err;
  1107.     ProcessInfoRec        info;
  1108.     ProcessSerialNumber    curPSN;
  1109.  
  1110.     err = GetCurrentProcess(&curPSN);
  1111.     
  1112.     info.processInfoLength = sizeof(ProcessInfoRec);    // ALWAYS USE sizeof!
  1113.     info.processName = appName;                            // so it returned somewhere
  1114.     info.processAppSpec = NULL;                            // I don't care!
  1115.  
  1116.     err = GetProcessInformation(&curPSN, &info);
  1117. }
  1118.  
  1119. void GetAppFSSpec(FSSpec *appSpec)    {
  1120.     OSErr                err;
  1121.     Str255                appName;
  1122.     ProcessInfoRec        info;
  1123.     ProcessSerialNumber    curPSN;
  1124.  
  1125.     err = GetCurrentProcess(&curPSN);
  1126.     
  1127.     info.processInfoLength = sizeof(ProcessInfoRec);    // ALWAYS USE sizeof!
  1128.     info.processName = appName;                            // so it returned somewhere
  1129.     info.processAppSpec = appSpec;                        // so it can get returned!
  1130.  
  1131.     err = GetProcessInformation(&curPSN, &info);
  1132. }
  1133.  
  1134. /* ••• Apple event routines begin here ••• */
  1135.  
  1136. /*
  1137.     This routine will create a targetDesc for sending to self.
  1138.  
  1139.     We take IM VI's advice and use the typePSN form with 
  1140.     kCurrentProcess as the targetPSN.
  1141. */
  1142. OSErr GetTargetFromSelf (AEAddressDesc *targetDesc)
  1143. {
  1144.     ProcessSerialNumber    psn;
  1145.  
  1146.     psn.highLongOfPSN     = 0;
  1147.     psn.lowLongOfPSN     = kCurrentProcess;
  1148.  
  1149.     return( AECreateDesc(typeProcessSerialNumber, (Ptr)&psn, sizeof(ProcessSerialNumber), targetDesc) );
  1150. }
  1151.  
  1152. /* This routine will create a targetDesc using the apps signature */
  1153. OSErr GetTargetFromSignature (OSType processSig, AEAddressDesc *targetDesc)
  1154. {
  1155.     return( AECreateDesc(typeApplSignature, (Ptr)&processSig, sizeof(processSig), targetDesc) );
  1156. }
  1157.  
  1158.  
  1159. void SendQuitToSelf (void)
  1160. {
  1161.     OSErr            err;
  1162.     AEAddressDesc    theTarget;
  1163.     AppleEvent        quitAE, replyAE;
  1164.  
  1165. /*
  1166.     First we create the target for the event.   We call another
  1167.     utility routine for creating the target.
  1168. */
  1169.     err = GetTargetFromSelf(&theTarget);
  1170.     if (err == noErr) {
  1171.         /* Next we create the Apple event that will later get sent. */
  1172.         err = AECreateAppleEvent(kCoreEventClass, kAEQuitApplication, &theTarget, kAutoGenerateReturnID, kAnyTransactionID, &quitAE);
  1173.  
  1174.         if (err == noErr) {
  1175.             /*
  1176.                 and finally send the event
  1177.                 Since we are sending to ourselves, no need for reply.
  1178.             */
  1179.             err = AESend(&quitAE, &replyAE, kAENoReply + kAECanInteract, kAENormalPriority, 3600, NULL, NULL);
  1180.  
  1181.             /*
  1182.                 NOTE: Since we are not requesting a reply, we do not need to
  1183.                 need to dispose of the replyAE.  It is there simply as a 
  1184.                 placeholder.
  1185.             */
  1186.         }
  1187.  
  1188.         /* and of course dispose of the quit AEVT itself */
  1189.         err = AEDisposeDesc(&quitAE);
  1190.         err = AEDisposeDesc(&theTarget);
  1191.     }
  1192. }
  1193.  
  1194.  
  1195. void SendQuitToPSN (ProcessSerialNumber *psn)
  1196. {
  1197.     OSErr            err;
  1198.     AEAddressDesc    theTarget;
  1199.     AppleEvent        quitAE;
  1200.  
  1201. /*
  1202.     First we create the target for the event.   We call another
  1203.     utility routine for creating the target.
  1204. */
  1205.     err = AECreateDesc(typeProcessSerialNumber, psn, sizeof(ProcessSerialNumber), &theTarget);
  1206.     if (err == noErr) {
  1207.         /* Next we create the Apple event that will later get sent. */
  1208.         err = AECreateAppleEvent(kCoreEventClass, kAEQuitApplication, &theTarget, kAutoGenerateReturnID, kAnyTransactionID, &quitAE);
  1209.  
  1210.         if (err == noErr) {
  1211.             /*
  1212.                 and finally send the event
  1213.                 Since we are sending to ourselves, no need for reply.
  1214.             */
  1215.             err = AESend(&quitAE, 0L, kAENoReply + kAECanInteract, kAENormalPriority, 3600, NULL, NULL);
  1216.  
  1217.             /*
  1218.                 NOTE: Since we are not requesting a reply, we do not need to
  1219.                 need to dispose of the replyAE.  It is there simply as a 
  1220.                 placeholder.
  1221.             */
  1222.         }
  1223.  
  1224.         /* and of course dispose of the quit AEVT itself */
  1225.         err = AEDisposeDesc(&quitAE);
  1226.         err = AEDisposeDesc(&theTarget);
  1227.     }
  1228. }
  1229.  
  1230.  
  1231. void SendRestartToFinder()
  1232. {
  1233.     OSErr            err;
  1234.     OSType            finderType = 'MACS';
  1235.     AEAddressDesc    theTarget;
  1236.     AppleEvent        restartAE;
  1237.  
  1238. /*
  1239.     First we create the target for the event.   We call another
  1240.     utility routine for creating the target.
  1241. */
  1242.     err = AECreateDesc(typeApplSignature, &finderType, sizeof(OSType), &theTarget);
  1243.     if (err == noErr) {
  1244.         /* Next we create the Apple event that will later get sent. */
  1245.         err = AECreateAppleEvent('FNDR', 'rest', &theTarget, kAutoGenerateReturnID, kAnyTransactionID, &restartAE);
  1246.  
  1247.         if (err == noErr) {
  1248.             /*
  1249.                 and finally send the event
  1250.                 Since we are sending to ourselves, no need for reply.
  1251.             */
  1252.             err = AESend(&restartAE, 0L, kAENoReply + kAECanInteract, kAENormalPriority, 3600, NULL, NULL);
  1253.  
  1254.             /*
  1255.                 NOTE: Since we are not requesting a reply, we do not need to
  1256.                 need to dispose of the replyAE.  It is there simply as a 
  1257.                 placeholder.
  1258.             */
  1259.         }
  1260.  
  1261.         /* and of course dispose of the quit AEVT itself */
  1262.         err = AEDisposeDesc(&restartAE);
  1263.         err = AEDisposeDesc(&theTarget);
  1264.     }
  1265. }
  1266.  
  1267.  
  1268. void SendShutDownToFinder()
  1269. {
  1270.     OSErr            err;
  1271.     OSType            finderType = 'MACS';
  1272.     AEAddressDesc    theTarget;
  1273.     AppleEvent        shutDownAE;
  1274.  
  1275. /*
  1276.     First we create the target for the event.   We call another
  1277.     utility routine for creating the target.
  1278. */
  1279.     err = AECreateDesc(typeApplSignature, &finderType, sizeof(OSType), &theTarget);
  1280.     if (err == noErr) {
  1281.         /* Next we create the Apple event that will later get sent. */
  1282.         err = AECreateAppleEvent('FNDR', 'shut', &theTarget, kAutoGenerateReturnID, kAnyTransactionID, &shutDownAE);
  1283.  
  1284.         if (err == noErr) {
  1285.             /*
  1286.                 and finally send the event
  1287.                 Since we are sending to ourselves, no need for reply.
  1288.             */
  1289.             err = AESend(&shutDownAE, 0L, kAENoReply + kAECanInteract, kAENormalPriority, 3600, NULL, NULL);
  1290.  
  1291.             /*
  1292.                 NOTE: Since we are not requesting a reply, we do not need to
  1293.                 need to dispose of the replyAE.  It is there simply as a 
  1294.                 placeholder.
  1295.             */
  1296.         }
  1297.  
  1298.         /* and of course dispose of the quit AEVT itself */
  1299.         err = AEDisposeDesc(&shutDownAE);
  1300.         err = AEDisposeDesc(&theTarget);
  1301.     }
  1302. }
  1303.  
  1304.